/*
 *  CSE 40166, Computer Graphics, Fall 2013
 *  Assignment 4, Part 2
 *
 *  File: OBJ_Parser.cpp
 *
 *  Kevin Sonnen
 *
 *  Description: Contains declaration of classes used to parse an obj file
 *
 */

// Include libraries
#include "OBJ_Parser.h"

////////////////////////////////////////////
// OBJ_Parser Member Functions
////////////////////////////////////////////

//
// Object getObject
//
// Return the object member variable, which contains all of the geometry 
// data after a file is parsed
//
Object
OBJ_Parser::getObject() {
	return mObject;
}

//
// void readMaterial(const std::string & line)
//
// Read a line from an OBJ File starting with usemtl or mtllib
// Note: This currently only is called if a line starts with u or m, not usemtl or mtllib
//
void 
OBJ_Parser::readMaterial(const std::string &line) {
	int spaceLocation = line.find(' ');
	while (line[spaceLocation+1] == ' ') {
		++spaceLocation;
	}
	// Put the values portion of the string into lineValues
	std::string filename = line.substr(spaceLocation+1);
	#ifdef VERBOSE
	std::cout << "Using material " << filename << std::endl;
	#endif
}

//
// Read a line from an OBJ File starting with f
//
// void readFace(const std::string & line)
//
void 
OBJ_Parser::readFace(const std::string &line) {
	face new_face;
	face new_face1;
	
	#ifdef VERBOSE 
	std::cout << "Line: " << line << std::endl; 
	#endif
	// Find the last space before the numbers start
	int spaceLocation = line.find(' ');
	while (line[spaceLocation+1] == ' ') {
		++spaceLocation;
	}
	// Put the values portion of the string into lineValues
	std::string lineValues = line.substr(spaceLocation+1);
	#ifdef VERBOSE
	std::cout << "Line Values: " << lineValues << std::endl;
	#endif
	std::string firstSet = lineValues.substr(0, lineValues.find(' '));
	#ifdef VERBOSE
	std::cout << "First set face: " << firstSet << std::endl;
	#endif
	
	unsigned int vertexIndex[5];
	unsigned int textureIndex[5];
	unsigned int normalIndex[5];
	int numVertices;
	bool multipleFaces = false;
    
    // Figure out the formatting of the numbers
    // Note: We assume that all groups of numbers in a face line have the same formatting
	if(firstSet.find('/') != std::string::npos) {
		if(firstSet[firstSet.find('/')+1] == '/') {
			// Format of firstSet: 111//222
			// Case: Vertex/Normal
			#ifdef VERBOSE
			std::cout << "Vertex/normal\n";
			#endif
			
			// Figure out how many pairs of indices are specified
			// We can deal with either 3 or 4, but anything else should throw an error
			numVertices = sscanf(lineValues.c_str(), "%d//%d %d//%d %d//%d %d//%d %d//%d", &vertexIndex[0], &normalIndex[0], &vertexIndex[1], &normalIndex[1], &vertexIndex[2], &normalIndex[2], &vertexIndex[3], &normalIndex[3], &vertexIndex[4], &normalIndex[4]);
			switch(numVertices) {
				case 6:
					#ifdef VERBOSE
					std::cout << "3 vertices\n";
					#endif
					new_face.setVertexIndices(vertexIndex[0]-1, vertexIndex[1]-1, vertexIndex[2]-1);
					new_face.setNormalIndices(normalIndex[0]-1, normalIndex[1]-1, normalIndex[2]-1);
					break;
				case 8:
					#ifdef VERBOSE
					std::cout << "4 vertices\n";
					#endif
					multipleFaces = true;
					// Break into two triangular faces
					new_face.setVertexIndices(vertexIndex[0]-1, vertexIndex[1]-1, vertexIndex[2]-1);
					new_face.setNormalIndices(normalIndex[0]-1, normalIndex[1]-1, normalIndex[2]-1);
					
					new_face1.setVertexIndices(vertexIndex[0]-1, vertexIndex[2]-1, vertexIndex[3]-1);
					new_face1.setNormalIndices(normalIndex[0]-1, normalIndex[2]-1, normalIndex[3]-1);
					break;
				default:
					#ifdef ERROR
					std::cout << "Error: improper formatting of face: " << line << std::endl;
					#endif
					return;
			}
		} else {
			// Format of firstSet: 111/222 or 111/222/333. We need to figure out which.
			int tempValues[3];
			int numValues = sscanf(firstSet.c_str(), "%d/%d/%d", &tempValues[0], &tempValues[1], &tempValues[2]);
			switch(numValues) {
				case 2:
					// Format of firstSet: 111/222
					// Case: Vertex/texture-coordinate
					#ifdef VERBOSE
					std::cout << "Vertex/texture-coordinate\n";
					#endif
			
					// Figure out how many pairs of indices are specified
					// We can deal with either 3 or 4, but anything else should throw an error
					numVertices = sscanf(lineValues.c_str(), "%d/%d %d/%d %d/%d %d/%d %d/%d", &vertexIndex[0], &textureIndex[0], &vertexIndex[1], &textureIndex[1], &vertexIndex[2], &textureIndex[2], &vertexIndex[3], &textureIndex[3], &vertexIndex[4], &textureIndex[4]);
					switch(numVertices) {
						case 6:
							#ifdef VERBOSE
							std::cout << "3 vertices\n";
							#endif
							new_face.setVertexIndices(vertexIndex[0]-1, vertexIndex[1]-1, vertexIndex[2]-1);
							new_face.setTextureIndices(textureIndex[0]-1, textureIndex[1]-1, textureIndex[2]-1);
							break;
						case 8:
							#ifdef VERBOSE
							std::cout << "4 vertices\n";
							#endif
							// Break into two triangular faces
							multipleFaces = true;
							new_face.setVertexIndices(vertexIndex[0]-1, vertexIndex[1]-1, vertexIndex[2]-1);
							new_face.setTextureIndices(textureIndex[0]-1, textureIndex[1]-1, textureIndex[2]-1);
							
							new_face1.setVertexIndices(vertexIndex[0]-1, vertexIndex[2]-1, vertexIndex[3]-1);
							new_face1.setTextureIndices(vertexIndex[0]-1, vertexIndex[2]-1, vertexIndex[3]-1);
							break;
						default:
							#ifdef ERROR
							std::cout << "Error: improper formatting of face: " << line << std::endl;
							#endif
							return;
					}
					break;
				case 3:
					// Format of firstSet: 111/222/333
					// Case: Vertex/texture-coordinate/normal
					#ifdef VERBOSE
					std::cout << "Vertex/texture-coordinate/normal\n";
					#endif
			
					// Figure out how many pairs of indices are specified
					// We can deal with either 3 or 4, but anything else should throw an error
					numVertices = sscanf(lineValues.c_str(), "%d/%d/%d %d/%d/%d %d/%d/%d %d/%d/%d %d/%d/%d", &vertexIndex[0], &textureIndex[0], &normalIndex[0], &vertexIndex[1], &textureIndex[1], &normalIndex[1], &vertexIndex[2], &textureIndex[2], &normalIndex[2], &vertexIndex[3], &textureIndex[3], &normalIndex[3], &vertexIndex[4], &textureIndex[4], &normalIndex[4]);
					switch(numVertices) {
						case 9:
							#ifdef VERBOSE
							std::cout << "3 vertices\n";
							#endif
							new_face.setVertexIndices(vertexIndex[0]-1, vertexIndex[1]-1, vertexIndex[2]-1);
							new_face.setTextureIndices(textureIndex[0]-1, textureIndex[1]-1, textureIndex[2]-1);
							new_face.setNormalIndices(normalIndex[0]-1, normalIndex[1]-1, normalIndex[2]-1);
							break;
						case 12:
							#ifdef VERBOSE
							std::cout << "4 vertices\n";
							#endif
							// Break up into two triangular faces
							multipleFaces = true;
							new_face.setVertexIndices(vertexIndex[0]-1, vertexIndex[1]-1, vertexIndex[2]-1);
							new_face.setTextureIndices(textureIndex[0]-1, textureIndex[1]-1, textureIndex[2]-1);
							new_face.setNormalIndices(normalIndex[0]-1, normalIndex[1]-1, normalIndex[2]-1);
							
							new_face1.setVertexIndices(vertexIndex[0]-1, vertexIndex[2]-1, vertexIndex[3]-1);
							new_face1.setTextureIndices(textureIndex[0]-1, textureIndex[2]-1, textureIndex[3]-1);
							new_face1.setNormalIndices(normalIndex[0]-1, normalIndex[2]-1, normalIndex[3]-1);
							break;
						default:
							#ifdef ERROR
							std::cout << "Error: improper formatting of face: " << line << std::endl;
							#endif
							return;
					}
					break;
				default:
					#ifdef ERROR
					std::cout << "Error: improper formatting of face: " << line << std::endl;
					#endif
					return;
			} // End of switch
		}
	} else {
		// Format of firstSet: 111
		// Case: Vertex
		#ifdef VERBOSE
		std::cout << "Vertex\n";
		std::cout << lineValues << std::endl;
		#endif
		
		numVertices = sscanf(lineValues.c_str(), "%d %d %d %d %d", &vertexIndex[0], &vertexIndex[1], &vertexIndex[2], &vertexIndex[3], &vertexIndex[4]);
		switch(numVertices) {
			case 3:
				#ifdef VERBOSE
				std::cout << "3 vertices\n";
				#endif
				new_face.setVertexIndices(vertexIndex[0]-1, vertexIndex[1]-1, vertexIndex[2]-1);
				break;
			case 4:
				#ifdef VERBOSE
				std::cout << "4 vertices\n";
				#endif
				multipleFaces = true;
				// Break up into two triangular faces
				new_face.setVertexIndices(vertexIndex[0]-1, vertexIndex[1]-1, vertexIndex[2]-1);
				new_face1.setVertexIndices(vertexIndex[0]-1, vertexIndex[2]-1, vertexIndex[3]-1);
				break;
			default:
				#ifdef ERROR
				std::cout << "Error: improper formatting of face: " << line << std::endl;
				#endif
				return;
		}
	}
	if(multipleFaces) {
		// Break into two triangles
		mObject.faces.push_back(new_face);
		mObject.faces.push_back(new_face1);
	} else {
		mObject.faces.push_back(new_face);
	}
	++mObject.numFaces; // Increment numFaces (Only by one even if we broke a quadrilateral into two)
}

//
// Read a line from an OBJ File starting with v
// Get three values in the format v[*] val1 val2 val3 and return them as a point struct
//
// point readVertices(const std::string & line)
//
point
OBJ_Parser::readVertices(const std::string &line) {
	// Find the last space before the numbers start
	int spaceLocation = line.find(' ');
	while (line[spaceLocation+1] == ' ') {
		++spaceLocation;
	}
	std::string lineValues = line.substr(spaceLocation+1);
	#ifdef VERBOSE
	std::cout << "Line values: " << lineValues << std::endl;
	#endif
	std::string firstValue = lineValues.substr(0, lineValues.find(' '));
	std::string secondValue = lineValues.substr(firstValue.length()+1, lineValues.find(' '));
	std::string thirdValue = lineValues.substr(lineValues.find_last_of(' ') + 1);

	point new_point;
	new_point.coord[0] = (float)atof(firstValue.c_str());
	new_point.coord[1] = (float)atof(secondValue.c_str());
	new_point.coord[2] = (float)atof(thirdValue.c_str());
	
	return new_point;
}

//
// Read a line from an OBJ File starting with v
// Get two values in the format vt val1 val2 and return them as a point_2d struct
//
// point_2d readTextureCoord(const std::string & line)
//
point_2d
OBJ_Parser::readTextureCoord(const std::string &line) {
	// Find the last space before the numbers start
	int spaceLocation = line.find(' ');
	while (line[spaceLocation+1] == ' ') {
		++spaceLocation;
	}
	std::string lineValues = line.substr(spaceLocation+1);
	#ifdef VERBOSE
	std::cout << "Line values: " << lineValues << std::endl;
	#endif
	std::string firstValue = lineValues.substr(0, lineValues.find(' '));
	std::string secondValue = lineValues.substr(firstValue.length()+1);

	point_2d new_point;
	// NOTE: When applying the example texture provided, it ended up being
	// flipped vertically. To remedy this, we can either flip the image itself,
	// or flip the y coordinate that we stored. We do that here if flipTextureCoords
	// is set to true.
	const bool flipTextureCoords = true;
	new_point.coord[0] = (float)atof(firstValue.c_str());
	new_point.coord[1] = (float)atof(secondValue.c_str());
	if (flipTextureCoords) {
		new_point.coord[1] = 1.0f - new_point.coord[1];
	}
	
	return new_point;
}

//
// Read an obj file, place its vertices and faces in vectors, and print out a count
//
// void parseOBJ(const std::string &filename)
//
bool 
OBJ_Parser::parseOBJ(const std::string &filename) {
	// Make sure data structures are empty
	mObject.vertices.clear();
	mObject.normals.clear();
	mObject.textures.clear();
	mObject.faces.clear();
	mObject.numFaces = 0;
	
	#ifdef VERBOSE
	std::cout << "Parsing " << filename << std::endl;
	#endif
	// Open the file
	std::ifstream objFile(filename.c_str());
	if(objFile.fail()) {
		#ifdef ERROR
		std::cout << "Error: Could not open " << filename << std::endl;
		#endif
		return false;
	}
	// Read the lines of the file
	std::string line;
	while(getline(objFile, line)) {
		if (!line.empty()) {
			switch(line[0]) {
				case 'v':	// Vertex
					switch(line[1]) {
						case ' ':
							mObject.addVertices(readVertices(line));
							break;
						case 't':	// Vertex Texture
							mObject.textures.push_back(readTextureCoord(line));
							break;
						case 'n':	// Vertex Normal
							mObject.normals.push_back(readVertices(line));
							break;
						default:
							#ifdef ERROR
							std::cout << "Value v" << line[1] << " unknown" << std::endl;
							#endif
							break;
					}
					break;
				case 'f':	// Face
					readFace(line);
					break;
				case 'm':	// mtllib
				case 'u':	// usemtl
					//readMaterial(line);
					break;
				case 'g':	// Group
					break;
				case ' ':	// Space
					break;
				case '#':	// Comment
					break;
				default:
					#ifdef ERROR
					std::cout << "Value " << line[0] << " unknown" << std::endl;
					#endif
					break;
			}
		}
	}
	// Close the file
	objFile.close();
	
	// Print out the results
	#ifdef VERBOSE
	std::cout << mObject.vertices.size() << " vertices" << std::endl;
	std::cout << mObject.textures.size() << " textures" << std::endl;
	std::cout << mObject.normals.size() << " normals" << std::endl;
	// Note: If we broke a quadrilateral into two triangles, we only count this as one face
	std::cout << mObject.numFaces << " faces" << std::endl;
	#endif
	float surfaceArea = 0;
	for (unsigned int i=0; i<mObject.faces.size(); ++i) {
		float area = mObject.faceArea(i);
		#ifdef VERBOSE
		std::cout << "Face " << i << " has area " << area << std::endl;
		#endif
		surfaceArea+= area;
	}
	#ifdef VERBOSE
	std::cout << "Total surface area of mesh: " << surfaceArea << std::endl;
	#endif
	
	return true;
}
